> < ^ Date: Fri, 19 Feb 1993 20:24:10 +0100
> < ^ From: Thomas Breuer <Thomas.Breuer@Math.RWTH-Aachen.DE >
^ Subject: [strings in GAP-3.2]

Dear Mrs. and Mr. Forum,
many things have changed from GAP-3.1 to GAP-3.2, and there are a few
cases where this means not only extensions but the possibility that a
function will cause strange results in GAP-3.2 although it worked well
in GAP-3.1. Maybe the most nasty case is the behaviour of strings.

As said in the announcement of GAP-3.2, now strings are also lists, thus
'IsList( <str> )' yields 'true' for a string <str>. Suppose you have a
function that shall append a suffix to strings, and whose argument can be
either a string or a list of strings, then in GAP-3.1 it may look like this.

gap> test := function( obj )
>     if IsList( obj ) then
>       return List( obj, y -> ConcatenationString( y, ".suffix" ) );
>     else
>       return ConcatenationString( obj, ".suffix" );
>     fi;
>    end;;
gap> test( "nolist" );
"nolist.suffix"
gap> test( [ "l", "i", "s", "t" ] );
[ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ]

In GAP-3.2 this function does not work.

gap> test( "nolist" );
Error, Append: <list2> must be a list at
Append( res, str ) ... in
ConcatenationString( y, ".suffix" ) called from
fun( i ) called from
List( obj, function ( y ) ... end ) called from
test( "nolist" ) called from
main loop
brk> y;
'n'
brk> quit;
gap> test( [ "l", "i", "s", "t" ] );
[ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ]
gap> [ 'l', 'i', 's', 't' ];
"list"
gap> List( last );
[ "l", "i", "s", "t" ]

This is because the string '"nolist"' now is a list, and 'test' tries to
concatenate its entries with the suffix; but the entries of '"nolist"'
aren't strings but characters (which are printed in single quotes).
So a string is equivalent to the list of its characters. On the other
hand, a list of strings is not itself a string; as in GAP-3.1, the function
'List' returns for a string the list of strings containing single letters.

Of course this is not the real problem, the solution is simply to ask for
the more special case of being a string first. But there is a problem, and
as everybody will suspect who read the messages to the GAP forum last week,
it is a problem with empties.

gap> IsList( "" ); IsString( "" );
true
true
gap> IsList( [] ); IsString( [] );
true
true
gap> [] = "";
true

(By the way, what should the function 'test' defined above return in case
of input '""' or '[]'? In GAP-3.1 there was a well-defined difference.)
Empty string '""' and empty list '[]' are the same now, with one exception:
In contrary to nonempty lists of characters, the empty list is not converted
into a string, it is printed as '[ ]'. And printing one of these objects
is the problem. Suppose you want to write a program that prints strings
enclosed in '"', and prints other objects as they are printed by 'Print',
in GAP-3.1 this function worked.

gap> MyPrint := function( obj )
>      if IsString( obj ) then
>        Print( "\"", obj, "\"\n" );
>      else
>        Print( obj, "\n" );
>      fi;
>    end;;
gap> MyPrint( [] );
"[  ]"
gap> MyPrint( "" );
""

We want the empty list to be printed as '[ ]', and the empty string as '""',
so we must be able to distinguish the two objects. The only solution is to
ask for the information that also GAP-3.2 uses to distinguish them.
This can be done using the function 'TYPE' that returns '"string"' for '""',
and '"list"' for '[]'. Note that this is one of the VERY FEW situations
where an undocumented function like 'TYPE' is of interest for the user,
normally one should avoid using such functions.

gap> MyPrint := function( obj )
>      if IsString( obj ) and TYPE( obj ) = "string" then
>        Print( "\"", obj, "\"\n" );
>      else
>        Print( obj, "\n" );
>      fi;
>    end;;
gap> MyPrint( [] );
[  ]
gap> MyPrint( "" );
""

Thomas Breuer
(sam@ernie.math.rwth-aachen.de)


> < [top]